#ifndef __DYNTYPE_HH__
#define __DYNTYPE_HH__

#include <string>
#include <vector>
#include <iostream>
using namespace std;

#include <math.h>
#include <limits.h>
#include <misc/dictmap.h>
#include <misc/dictvector.h>
#include <misc/stringutils.h>
#include <dyntype/membuf.h>

class DynType
{
public:
  DynType(const string& name);
  virtual ~DynType();

  const string& get_name() const;
  size_t get_size() const;

  bool is_none_rep(const membuf_t& v_buf);

  virtual void print_obj(ostream& out, const membuf_t& v_buf) = 0;
  virtual bool equal_obj(const membuf_t& l_v_buf, const membuf_t& r_v_buf) = 0;
  virtual bool less_obj(const membuf_t& l_v_buf, const membuf_t& r_v_buf) = 0;

  virtual void print(ostream&) const = 0;
  virtual void save(ostream&) const = 0;
  virtual void load(istream&) = 0;
  void dump() const;

protected:
  string name;
  size_t obj_size;
  
  friend class DynObject;
  friend class CompoundDynType;
  friend class MMapDataField;
  friend class FileDataField;
  membuf_t none_rep;
};

template <typename T> DynType* get_dyntype();

class DynTypeManager
{
public:
  static DynTypeManager* get();
  
  void clear();
  void register_dyntype(DynType* dyntype);
  DynType* get_dyntype(const string& dyntypename);

  void save_dyntype(ostream& out, const string& dyntypename);
  string load_and_register_dyntype(istream& in);

private:
  static DynTypeManager* only_one;
  DictMap<string, DynType*> dyntypes;
};

inline DynType* DT(const char* dyntypename) {
  return DynTypeManager::get()->get_dyntype(dyntypename);
}

template <class T>
class IntegralDynType : public DynType
{
 public:
  IntegralDynType(const string& name); 

  void print_obj(ostream& out, const membuf_t& o_buf);
  bool equal_obj(const membuf_t& l_v_buf, const membuf_t& r_v_buf);
  bool less_obj(const membuf_t& l_v_buf, const membuf_t& r_v_buf);

  void print(ostream& out) const;
  void save(ostream& out) const;
  void load(istream& in);
};

class CharNDynType : public DynType
{
 public:
  CharNDynType(const string& name, size_t size);

  size_t get_nof_members() const;
  void snprintf_member(char* buf, size_t buf_len, size_t member_index,
		       const membuf_t& v_buf) const;

  void print_obj(ostream& out, const membuf_t& o_buf);
  bool equal_obj(const membuf_t& l_v_buf, const membuf_t& r_v_buf);
  bool less_obj(const membuf_t& l_v_buf, const membuf_t& r_v_buf);

  void print(ostream& out) const;
  void save(ostream& out) const;
  void load(istream& in);
};

template <int N>
struct CharN {
    explicit CharN(const string& s) { strncpy(c, s.c_str(), s.size()); }
    explicit CharN(const char* s) { strncpy(c, s, sizeof c); }
    void operator=(const char* s) { strncpy(c, s, sizeof c); }
    char c[N];
};

#define CharN_dyntype_tmpl(n) \
template <> inline DynType* get_dyntype<CharN<n> >() { \
  return DynTypeManager::get()->get_dyntype("char["#n"]"); \
} \
template <> inline DynType* get_dyntype<char[n]>() { \
  return DynTypeManager::get()->get_dyntype("char["#n"]"); \
}


CharN_dyntype_tmpl(1)
CharN_dyntype_tmpl(2)
CharN_dyntype_tmpl(3)
CharN_dyntype_tmpl(4)
CharN_dyntype_tmpl(5)
CharN_dyntype_tmpl(6)
CharN_dyntype_tmpl(7)
CharN_dyntype_tmpl(8)
CharN_dyntype_tmpl(9)
CharN_dyntype_tmpl(10)
CharN_dyntype_tmpl(11)
CharN_dyntype_tmpl(12)
CharN_dyntype_tmpl(13)
CharN_dyntype_tmpl(14)
CharN_dyntype_tmpl(15)
CharN_dyntype_tmpl(16)
#define MAX_CHAR_N_LEN 16

// .....................................................................

template <>
inline DynType* get_dyntype<int>() {
  return DynTypeManager::get()->get_dyntype("int");
}

template <>
inline DynType* get_dyntype<double>() {
  return DynTypeManager::get()->get_dyntype("double");
}

template <class T, class V>
inline size_t offset(V T::* mptr) {
  return size_t(&(((T*)0)->*mptr));
}

struct CompoundDynTypeMember
{
  CompoundDynTypeMember();
  CompoundDynTypeMember(const string& member_name, 
			const string& member_dyntypename,
			size_t cardinality,
			size_t offset);

  size_t get_member_offset(const string& member, size_t cardinality_index) const;
  DynType* get_member_dyntype(const string& member) const;
  
  void print(ostream& out) const;
  void save(ostream& out) const;
  void load(istream& in);
  
  string member_name;
  DynType* member_dyntype;
  size_t cardinality;
  size_t offset;
};

class CompoundDynType : public DynType
{
public:
  CompoundDynType(const string& name);
  void setup_none_rep();

  size_t get_nof_members() const;
  const string& get_member_name(size_t member_index) const;
  void snprintf_member(char* buf, size_t buf_len, size_t member_index,
		       const membuf_t& v_buf) const;

  void print_obj(ostream& out, const membuf_t& v_buf);
  bool equal_obj(const membuf_t& l_v_buf, const membuf_t& r_v_buf);
  bool less_obj(const membuf_t& l_v_buf, const membuf_t& r_v_buf);

  size_t get_member_offset(const string& m, size_t cardinality_index) const;
  DynType* get_member_dyntype(const string& m) const;

  bool add_member(const string& member, 
		  const string& member_type,
		  size_t offset,
		  size_t cardinality);

  const CompoundDynTypeMember* get_member(const string& member_name) const;
  
  void save(ostream& out) const;
  void load(istream& in);
  void print(ostream& out) const;

 protected:
  CompoundDynType();
  DictVector<string, CompoundDynTypeMember> members;
};

// inlines
template <>
inline
IntegralDynType<int>::IntegralDynType(const string& name) : DynType(name) 
{
  this->obj_size = sizeof(int);
  this->none_rep.buf_len = sizeof(int);
  int* bi = new int[1]; bi[0] = INT_MAX;
  this->none_rep.buf = (char*)bi;
}

template <>
inline
IntegralDynType<double>::IntegralDynType(const string& name) : DynType(name) 
{
  this->obj_size = sizeof(double);
  this->none_rep.buf_len = sizeof(double);
  double* d = new double[1]; *d = sqrt(-1.0);
  this->none_rep.buf = (char*)d;
}

template <class T>
inline
void IntegralDynType<T>::print_obj(ostream& out, const membuf_t& o_buf) 
{
  out << *(T*)o_buf.buf;
}

template <class T>
inline
bool 
IntegralDynType<T>::equal_obj(const membuf_t& l_v_buf, const membuf_t& r_v_buf) 
{
  return *(T*)l_v_buf.buf == *(T*)r_v_buf.buf;
}

template <class T>
inline
bool 
IntegralDynType<T>::less_obj(const membuf_t& l_v_buf, const membuf_t& r_v_buf) 
{
  return *(T*)l_v_buf.buf < *(T*)r_v_buf.buf;
}

template <class T>
inline
void IntegralDynType<T>::print(ostream& out) const 
{
  out << name;
}

template <class T>
inline
void IntegralDynType<T>::save(ostream& out) const 
{
  out << name;
}

template <class T>
inline
void IntegralDynType<T>::load(istream& in)
{
}

#endif
